home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 25
/
Cream of the Crop 25.iso
/
disk
/
mt220.zip
/
SOURCE.ZIP
/
MT.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1997-04-05
|
35KB
|
1,053 lines
/* Move To drive/directory.
Copyright 1996-7 Jason Hood
Started: 14 April, 1996.
Finished: 4 June.
Modified: 29 September to 1 November:
Changed treatment of previous directories;
Removed "-> sorting -> indexing -> done" from the scan;
Added the extension option;
Changed the nonexistant directory message;
Changed the [wr]long functions to #define;
Fixed a bug with find function dealing with star not finding a directory;
Added open functions to open a file and exit the program if unable;
Added a hardware error handler to ignore drive not ready;
Modified reverse & finddirs function;
Added "+" & "-" options for adding and removing directories.
9 to 11 February, 1997:
Added "##" to the make the new extension permanent, default extension;
Added return codes;
Can accept a trailing (back)slash;
Can accept a partial and then exact path (eg. "mt w /not/scanned");
Added the directory structure file to the display;
Modified to work with MTMem 2.00;
Changed setdrive to distinguish between unavailable and invalid.
2 March:
Corrected a bug dealing with trailing (back)slash.
27 to 1 April:
More than two previous directories allowed.
Select a partial directory by number.
List matches using number 0.
Will change drive as well as directory.
Allows use of slash ("/") as well as backslash ("\").
Can use multiple dots (eg. treats "..." as "..\..").
Previous directory can be selected by "mt;" (or "mt ;" if not so lazy).
Use more semicolons ("mt;;;") or a number ("mt;3") to select others.
Partial directory names, where searches always start from the root.
"mt @drives" will construct a directory structure file for drives. "mt @"
will update the directory structure file for drives already in the file.
"mt +[+][path]" will create and add path to the file. If the other plus is
present, it will move to the new path. "mt -path" will delete path and
remove it from the file (root is never deleted).
Default file is "mtdirs.dat" (with path specified via mtmem). It can be
changed using "mt #[#]ext ..." where "ext" is the new extension (ie.
"mtdirs.ext") and "..." are the normal options. The second '#' will make
the new extension permanent. If "ext" is omitted it will default to "dat".
Path and previous directories are stored in memory, allocated by mtmem.
Exit Status:
0 - Successful operation (including help & status)
1 - MTMem not loaded
2 - Unable to create/load the structure file
3 - Directory not found
4 - Cannot create directory
5 - Cannot delete directory
Acknowledgements: Tim Jones' WASTED.PAS for finding directories.
findmpx is a modified version of Ralf Brown's findtsrs.
You are free to use this code, or a portion thereof, as long as an
appropriate acknowledgement is made.
Questions, suggestions and comments to hoodj@topaz.cqu.edu.au.
*/
#include <dir.h>
#include <dos.h>
#include <string.h>
#include <fstream.h>
#include <iomanip.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#define version "2.20"
#define EXIT_SUCCESS 0
#define EXIT_NOMTMEM 1
#define EXIT_NOMTDIR 2
#define EXIT_UNFOUND 3
#define EXIT_NOCREAT 4
#define EXIT_NOREMOV 5
char mtdirs[MAXPATH], // Directory structure file
prevbuf[2*MAXDIR+1], // Previous directories buffer
**prev; // Previous directories pointers
int prevnum; // Number of previous directories
char olddir[MAXDIR], newdir[MAXDIR]; // The current and new directories
char** dirs; // Directories for each drive
int dirnum; // Number of directories on each drive
const unsigned MaxDirs = 1200; // Maximum number of directories/drive
int setdrive(char drive); // Set the active drive
void expand(char* path); // Expand the dots
int find(char* path); // Try and find path
int partdir(int num, char* partial[], int which); // Find a partial dir.
void reverse(char drive, const char* path); // Reverse path
int adddir(char* dir); // Add a directory (tree)
int remdir(char* dir); // Remove a directory (tree)
void deldir(); // Delete directory
void update(int operation); // Update the directory structure file
void copy(ifstream& is, ofstream& os, long size); // Copy size bytes
void dirfile(char* drives); // Create directory structure for drives
void finddirs(); // Find directories
int finddirec(struct ffblk& dir, const char* name = ""); // Find dir. name
long index(ofstream& os, char drive); // Index and write drive's directories
int sort(const void* a, const void* b);// How the directories are sorted
int subs(const char* path); // Number of directories in path
void help(); // Display help screen
// Modify mtdirs' extension to ext (ie. "mtdirs.dat" becomes "mtdirs.ext")
#define modext(ext) strcpy(strrchr(mtdirs, '.')+1, ext)
const long zero = 0; // Variable to simulate wlong(os, 0)
// Write a long variable to a file as a binary value (ie. four bytes)
#define wlong(os, num) os.write((char*)&num, sizeof(long))
// Read a long variable from a file as a binary value (ie. four bytes)
#define rlong(is, num) is.read((char*)&num, sizeof(long))
// Open the mtdirs file for reading. If it fails, exit the program with an
// error message.
void open(ifstream& is, const char* name = mtdirs) {
is.open(name, ios::in | ios::binary);
if (is) return;
cout << "Unable to open \"" << name << "\".\n";
exit(EXIT_NOMTDIR);
}
// Open the mtdirs file for writing. If it fails, exit the program with an
// error message.
void open(ofstream& os, const char* name = mtdirs) {
os.open(name, ios::out | ios::binary);
if (os) return;
cout << "Unable to create \"" << name << "\".\n";
exit(EXIT_NOMTDIR);
}
// Hardware error handler for drive not ready - just ignore it.
int handler(int, int, int, int) {
hardretn(_HARDERR_IGNORE); // Could become fail
return 0;
}
// Find the multiplex number associated with MTMem. Return -1 if not installed.
int findmpx() {
char far* sig;
union REGS regs;
for (int mpx = 0; mpx <= 255; mpx++) {
regs.h.ah = mpx;
regs.h.al = 0; // installation check
int86(0x2d, ®s, ®s);
if (regs.h.al == 0xff) { // installed?
sig = (char far*)MK_FP(regs.x.dx, regs.x.di);
if (_fstrncmp("Adoxa MTMem ", sig, 16) == 0) return mpx;
}
}
return -1;
}
int main(int argc, char* argv[]) {
int j;
int mtmem; // Multiplex number
if ((mtmem = findmpx()) == -1) {
cout << "You must run \"MTMem\" first.\n";
return EXIT_NOMTMEM;
}
union REGS regs; // Get the address of the structure
regs.h.ah = mtmem; // file and previous directories
regs.h.al = 0x10;
int86(0x2d, ®s, ®s);
unsigned prevend = regs.x.ax + MAXPATH+2*MAXDIR; // Memory limit
movedata(regs.x.dx, regs.x.ax, _DS, (unsigned)mtdirs, MAXPATH+2*MAXDIR+1);
prevnum = 0;
char* prevcnt = prevbuf;
while (*prevcnt) { // Find the number of previous dirs
prevcnt = strchr(prevcnt, 0) + 1;
prevnum++;
}
prev = new char*[prevnum];
prevcnt = prevbuf;
for (j = 0; j < prevnum; j++) { // Point to the previous dirs
prev[j] = prevcnt;
prevcnt = strchr(prevcnt, 0) + 1;
}
harderr(handler); // Ignore an unready drive
olddir[0] = getdisk() + 'A'; // Retrieve the current directory
olddir[1] = ':'; // getcwd & _getdcwd do the same thing
olddir[2] = '\\'; // as these four statements, but they
getcurdir(0, olddir+3); // have more associated rigmarole
if (argc == 1) { // No parameters
cout << "\n Current directory = " << olddir;
if (prevnum) {
cout << "\n Previous directory = " << prev[0];
for (j = 1; j < prevnum; j++)
cout << endl << setw(25) << (j+1) << " = " << prev[j];
}
cout << "\n"
"\n Directory structure file = " << mtdirs
<< endl;
return EXIT_SUCCESS;
}
char* &dir = argv[1]; // Easier to type
if (*dir == '?' || dir[1] == '?') { // First or second character help
help(); // (switch char of your choice)
return EXIT_SUCCESS;
}
int moved = 0, // Flag to stop processing com. line
exit_code = EXIT_SUCCESS;
while (!moved) {
if (*dir == '#') { // A different directory structure file
int permanent = 0;
if (dir[1] == '#') { // A permament change
++dir;
permanent = 1;
}
if (dir[1] == 0) modext("dat");
else modext(dir+1);
if (permanent)
movedata(_DS, (unsigned)mtdirs, regs.x.dx, regs.x.ax, strlen(mtdirs)+1);
if (--argc == 1) return EXIT_SUCCESS;
argv++; dir = argv[1];
}
switch (*dir) {
case '@': // (Re)construct directory structure
dirfile(dir+1);
break;
case '+': // Add a directory (tree) to the file
moved = (dir[1] == '+'); // and create it if it doesn't exist
if (moved) dir++; // Skip the second plus
if (!adddir(dir+1)) {
cout << "Unable to create \"" << newdir << "\".\n";
exit_code = EXIT_NOCREAT;
}
break;
case '-': // Remove a directory (tree) from the
if (dir[1]) // file and delete it, but don't allow
if (!remdir(dir+1)) { // "mt -" in case of accident
cout << "Unable to remove \"" << newdir << "\".\n";
exit_code = EXIT_NOREMOV;
}
break;
case ';': // Use a previous directory
if (isdigit(dir[1])) { // A number was specified
if ((j = atoi(dir+1)) == 0) j = INT_MAX;
}
else { // else count semicolons
for (j = 1; dir[j] == ';'; j++);
}
j--;
if (j >= prevnum) strcpy(newdir, olddir);
else strcpy(newdir, prev[j]);
moved = 1;
break;
default:
moved = 1;
char* endp;
int which = (int)strtol(dir, &endp, 10);
if (*endp == ',' || *endp == '*') argv[1] = endp+1;
else {
which = 1;
if (argc == 2) if (find(dir)) {
moved = 2; // Find the full path for prev. dirs.
break;
}
}
int exact = 0;
if (*argv[argc-1] == '/' || *argv[argc-1] == '\\') {
argc--;
exact = 1;
}
int result = partdir(argc-1, argv+1, which);
if (result) {
if (result == 1) cout << "No match found.\n";
else cout << char(result) << ": has not been scanned.\n";
return EXIT_UNFOUND;
}
if (which == 0) return EXIT_SUCCESS;
if (exact) {
strcat(newdir, strupr(argv[argc]));
find(newdir);
}
}
if (!moved) { // No movement, so check next parameter
if (--argc == 1) return exit_code;// No more parameters, exit
argv++; dir = argv[1]; // Point to the next
}
}
int result = setdrive(*newdir);
if (result) {
cout << *newdir << ((result == 1) ? ": is unavailable.\n" :
": is an invalid drive.\n");
return EXIT_UNFOUND;
}
if (chdir(newdir) == -1) {
cout << '\"' << newdir << "\" is an invalid path.\n";
setdrive(*olddir);
return EXIT_UNFOUND;
}
if (moved == 2) { // Get the full path for below
newdir[2] = '\\';
getcurdir(0, newdir+3);
}
if (strcmp(newdir, olddir)) {
int len = strlen(olddir)+1;
movedata(_DS, (unsigned)olddir, regs.x.dx, regs.x.ax+MAXPATH, len);
regs.x.ax += MAXPATH+len;
for (j = 0; j < prevnum; j++) {
if (strcmp(newdir, prev[j]) && strcmp(olddir, prev[j])) {
len = strlen(prev[j])+1;
if ((regs.x.ax + len) > prevend) break;
movedata(_DS, (unsigned)prev[j], regs.x.dx, regs.x.ax, len);
regs.x.ax += len;
}
}
// pokeb doesn't work right - doesn't seem to like regs.x.dx
*((char far*)MK_FP(regs.x.dx, regs.x.ax)) = 0;
}
return EXIT_SUCCESS;
}
// Set the active drive to "drive" (which is assumed to be capital).
// Return:
// 0 for success;
// 1 for unavailable drive (eg. no disc present);
// 2 for invalid drive (eg. LASTDRIVE exceeded).
int setdrive(char drive) {
drive -= 'A';
setdisk(drive);
if (getdisk() != drive) return 2;
if (chdir(".") == -1) { // Simple test for disc present
setdisk(*olddir - 'A'); // Restore the old one if not
return 1;
}
return 0;
}
// Copy path to newdir, adding the current drive if no drive is specified,
// replacing '/' with '\', expanding the dots at the start and removing the
// trailing backslash.
void expand(char* path) {
if (path[1] == ':') { // A drive has been specified
*newdir = toupper(*path); // Make sure it's uppercase
path += 2; // Point past it
}
else *newdir = *olddir; // Use current drive
newdir[1] = ':';
if (*path == 0) { // Only a drive has been specified
newdir[2] = '.'; // so select the current directory
newdir[3] = 0; // on the new drive, as chdir("C:")
return; // (for example) will not work
}
int j = 2;
for (int dots = 1; *path == '.'; dots++, path++) {
if (dots > 2) { // Expand the dots by adding "\."
newdir[j++] = '\\'; // at the appropriate positions
newdir[j++] = '.'; // (Eg. for "..." the first two are
} // copied, then "\." is added, then
newdir[j++] = '.'; // the third dot is copied, yielding
} // "..\..")
int k = strlen(path)-1;
// Remove the ending (back)slash, but only if it's not the root
if ((k > 1 || j > 2) && (path[k] == '/' || path[k] == '\\')) path[k--] = 0;
for (; k >= 0; k--)
if (path[k] == '/') path[k] = '\\';
strcpy(newdir+j, path); // Copy the rest of the path
}
// See if path exists. It may end in a "*", in which case the first directory
// that matches will be selected. If the path does not exist, but it was
// expected to (parent shortcut, ending in star, or containing multiple paths)
// then there is no need for a search, so say it exists anyway.
// Returns 1 if path exists, 0 if not.
int find(char* path) {
struct ffblk dir;
int star = (path[strlen(path)-1] == '*'); // End in star?
expand(path);
if (finddirec(dir, newdir)) { // The path doesn't exist
if (newdir[2] == '.' || star || strchr(newdir, '\\')) return 1;
return 0;
}
if (star) { // Expand the name found
for (int j = strlen(newdir)-2; // Find where the name starts
j >= 0 && // The very beginning or
newdir[j] != '\\' &&
newdir[j] != ':'; // after the drive
j--);
strcpy(newdir+j+1, dir.ff_name); // Replace with the actual name
}
return 1;
}
// From the partial names try and find the which'th match. If the first name
// contains a drive specification then only that drive will be searched. If
// which is 0 then a numbered list of all matches will be displayed.
// Return 0 for success, 1 for no match, or the drive letter if that drive
// has not been scanned.
int partdir(int num, char* partial[], int which) {
ifstream mt; open(mt);
char drive, drv, // Drive to search, current drive
let, // First letter to match
path[MAXDIR], // A possible path
*dir; // Subdirectory to match
long index, next = 0; // File positions
int found, // Pretty much self-explanatory
*partlen = new int[num]; // Lengths of the partial names
for (int j = 0; j < num; j++)
partlen[j] = strlen(strupr(partial[j]));
if (partial[0][1] == ':') { // A drive has been specified
drive = *partial[0]; // so only search its directories
partial[0] += 2; // Skip past it to the first name
partlen[0] -= 2;
}
else drive = 0; // No drive, so search all of them
do { // For each drive required to search
mt.seekg(next); // Point to the drive
mt.get(drv); // Get this drive letter
rlong(mt, next); // Pointer to next drive
if (drive) { // Find the drive we want
while (next && drv != drive) {
mt.seekg(next);
mt.get(drv);
rlong(mt, next);
}
if (drv != drive) return drive; // Drive not in file
}
if (isalpha(let = *partial[num-1])) { // First name start with a letter?
mt.seekg((let-'A') * sizeof(long), ios::cur);
rlong(mt, index); // Then find the appropriate position
if (index == 0) continue; // No directories start with this letter
mt.seekg(index); // Starting position
}
else mt.seekg(26 * sizeof(long), ios::cur); // Start straight after indices
mt.getline(path, MAXDIR);
while (let == *path) {
found = 0; // Number of matches found
dir = path; // First subdirectory
for (int j = num-1; j >= 0 &&
!strncmp(partial[j], dir, partlen[j]); j--) {
found++;
dir = strchr(dir, '/'); // Find the next subdirectory
if (!dir) break; // The path has reached the root
dir++; // Point past the slash
}
if (found == num && --which <= 0) {// A successful match
reverse(drv, path); // Put the full path into newdir
if (which < 0) cout << -which << " = " << newdir << endl;
else
if (strcmp(newdir, olddir)) { // Only successful if not already here
next = 0; // To terminate the do loop
break; // To terminate the while loop
}
else ++which;
}
mt.getline(path, MAXDIR); // Try another match
}
} while (!drive && next); // Drive not specified and more exist
delete []partlen;
return (which <= 0) ? 0 : 1;
}
// Reverse path into newdir. If drive is null then reverse a DOS path (\) into
// mt's path (/), else add drive and reverse mt into DOS.
void reverse(char drive, const char* path) {
char *beg, // Where to place the current subdir.
sep1, sep2; // The separators
int j, end = strlen(path)-1; // Subdir's start and end
if (drive) {
newdir[0] = drive;
newdir[1] = ':';
newdir[2] = '\\';
beg = newdir+3;
sep1 = '/'; sep2 = '\\';
}
else {
beg = newdir;
sep1 = '\\'; sep2 = '/';
}
for (j = end-1; j > 0; j--) { // Search backwards for a separator
if (path[j] == sep1) { // Found one, so from here to the
memcpy(beg, path+j+1, end-=j); // last one is the subdir
beg += end;
*(beg++) = sep2;
end = --j; // Ready for the next
}
}
memcpy(beg, path, ++end); // Final subdirectory
*(beg+end) = 0;
}
// Add a directory (tree) to the file and create it if necessary. Unlike md
// this can create two directories in one hit. eg. "mt +this/that" is
// equivalent to "md this" "md this\that". If path is an empty string then add
// the current directory.
// Returns 1 for success or 0 for unable to create.
int adddir(char* path) {
char *sep = newdir+2, // Current directory to create
*start = NULL, // Start of the tree
orig[MAXDIR], // Current path on another drive
*ex; // To remember newdir
int success = 1;
expand(path);
if (*newdir != *olddir) { // It's on a different drive
if (setdrive(*newdir)) return 0; // so change to it and
*orig = '\\'; // remember its current directory
getcurdir(0, orig+1);
}
if (chdir(newdir) == -1) { // It doesn't already exist
do sep = strchr(sep+1, '\\'); // Skip past the dot directories
while (*(sep-1) == '.');
while (sep) { // Branch down the tree
*sep = 0;
if (mkdir(newdir) == 0) {
if (!start) start = strdup(newdir);
}
else if (errno == ENOENT) { // Bad name
if (start) { // Some directories were made
success = 0;
break;
}
else {
setdrive(*olddir);
return 0;
}
}
*sep = '\\';
sep = strchr(sep+1, '\\');
}
if (mkdir(newdir) == -1) {
if (!start) {
setdrive(*olddir);
return 0;
}
success = 0;
}
if (start) chdir(start);
else chdir(newdir);
}
ex = strdup(newdir); // newdir corrupted by update
update(0); // Add the directory/ies to the file
strcpy(newdir, ex);
free(ex);
if (*newdir != *olddir) {
chdir(orig);
setdrive(*olddir);
}
else chdir(olddir);
return success;
}
// Remove path from the file and delete it from disk if the first character
// is not "-". ie. "mt -path" will delete path and remove it from the file;
// "mt --path" will just remove path from the file. The root directory won't
// be deleted in any case.
// Returns 1 for success or 0 for doesn't exist / isn't a directory.
int remdir(char* path) {
char *ex, orig[MAXDIR]; // Expanded path, other drive's dir.
int rem = (*path == '-'), // Removing only, no deletion
root;
if (rem) path++; // Skip the leading minus
expand(path);
if (*newdir != *olddir) { // As for adddir
if (setdrive(*newdir)) return 0;
*orig = '\\';
getcurdir(0, orig+1);
}
if (chdir(newdir) == -1) { // Change to the directory
setdrive(*olddir); // to be deleted
return 0;
}
ex = strdup(newdir); // update corrupts newdir
update(1); // Remove the directory from the file
getcurdir(0, newdir); // Test for root directory
root = *newdir;
strcpy(newdir, ex);
free(ex);
if (!rem && root) deldir(); // Delete the directory from the disk
if (*newdir != *olddir) { // (but only if it's not the root)
chdir(orig);
setdrive(*olddir);
}
else chdir(olddir);
if (!rem && root) if (rmdir(newdir) == -1) adddir(newdir);
return 1;
}
// Delete ALL files and subdirectories in the current drive/directory.
void deldir() {
struct ffblk find;
static int done;
done = findfirst("*.*", &find,
FA_ARCH | FA_HIDDEN | FA_RDONLY | FA_SYSTEM | FA_DIREC);
while (!done) {
if (find.ff_attrib & FA_DIREC) {
if (*find.ff_name != '.') { // Why do these have to be found?
chdir(find.ff_name);
deldir();
chdir("..");
rmdir(find.ff_name);
}
}
else {
if (find.ff_attrib & FA_RDONLY) _dos_setfileattr(find.ff_name, 0);
unlink(find.ff_name);
}
done = findnext(&find);
}
}
// Update the directory structure file. Operation is 0 for adding directories,
// 1 for removing.
void update(int operation) {
ifstream mtin; open(mtin);
char drive = *newdir, drv;
long size, next = 0, last = 1, remdrive;
do { // See if the drive is in the file
remdrive = last; // For removing a drive
mtin.seekg(next);
mtin.get(drv);
last = mtin.tellg(); // For adding a drive
rlong(mtin, next);
} while (drv != drive && next);
if (drv != drive && operation == 1) { // Drive not in file and I'm removing
mtin.close(); // so there's nothing to do
return;
}
char *mttmp = strdup(mtdirs); // Remember the filename
modext("$~$"); // and create a temporary file
ofstream mtout; open(mtout);
size = (drv == drive ? mtin.tellg()-sizeof(long)-sizeof(char) :
(mtin.seekg(0, ios::end), mtin.tellg()));
mtin.seekg(0); // Copy all the drives before the one
copy(mtin, mtout, size); // that's being changed
mtin.seekg(sizeof(char) + 27*sizeof(long), ios::cur);
char path[MAXDIR];
int root, start, num, j;
getcurdir(0, path); // Check for the root directory
root = *path; // !root means root directory
if (drv != drive)
cout << "Adding drive " << drive << " to \"" << mttmp << "\" : ";
else if (!root) {
if (operation == 0)
cout << "Updating drive " << drive << " of \"" << mttmp << "\" : ";
else
cout << "Removing drive " << drive << " from \"" << mttmp << "\".\n";
}
if (root || operation == 0) { // No point in scanning if removing
dirs = new char*[MaxDirs]; // an entire drive
dirnum = 0;
finddirs();
if (!root || drv != drive) cout << dirnum << " directories.\n";
qsort((void*)dirs, dirnum, sizeof(dirs[0]), sort);
}
if (drv != drive) {
long temp = mtout.tellp(); // Set the old last drive to point
mtout.seekp(last); // to the new drive
wlong(mtout, temp);
mtout.seekp(temp);
last = index(mtout, drive);
}
else if (!root) {
mtin.seekg(next); // Skip what was in there
last = (operation == 0 ? index(mtout, drive) : remdrive);
}
else {
num = dirnum;
mtin.getline(path, MAXDIR);
while (*path && *path < *dirs[0]) { // Copy all the smaller directories
dirs[dirnum++] = strdup(path);
mtin.getline(path, MAXDIR);
}
start = 0;
while (*path && *path <= *dirs[num-1]) { // Check these directories
for (j = start; j < num; j++) {
if (*path < *dirs[j]) j = num-1; // It can't be in the list
else if (!strcmp(path, dirs[j])) {
start = j+1; // If it matches then
break; // skip all those before it
}
}
if (j == num) dirs[dirnum++] = strdup(path); // Wasn't found
mtin.getline(path, MAXDIR);
}
while (*path) { // Copy all the larger directories
dirs[dirnum++] = strdup(path);
mtin.getline(path, MAXDIR);
}
if (operation == 0) { // Sort new and old
qsort((void*)dirs, dirnum, sizeof(dirs[0]), sort);
last = index(mtout, drive);
}
else {
dirs += num; // Skip the directories to remove
dirnum -= num;
last = index(mtout, drive); // Index the remainder
dirs -= num; // Get them back for freeing
dirnum += num; // (The others are free'd in index)
for (j = 0; j < num; j++) free(dirs[j]);
}
}
if (!next) { // This was the last drive
mtout.seekp(last); // so indicate such (assuming that
wlong(mtout, zero); // not all drives have been removed)
}
else { // Copy all the other drives
// Adjustment for the new indices
long offset = mtout.tellp() - mtin.tellg();
while (next) {
mtin.get(drv);
mtout.put(drv);
rlong(mtin, next);
if (next) next += offset;
wlong(mtout, next);
for (j = 0; j < 26; j++) {
rlong(mtin, last);
if (last) last += offset;
wlong(mtout, last);
}
if (next) copy(mtin, mtout, next-mtin.tellg());
else {
last = mtin.tellg(); // The last drive is from here
mtin.seekg(0, ios::end); // to the end of the file, since
next = mtin.tellg(); // next is zero.
mtin.seekg(last);
copy(mtin, mtout, next-last);
next = 0; // To terminate the loop
}
}
}
delete []dirs;
mtin.close();
mtout.close();
unlink(mttmp); // Delete the original file
rename(mtdirs, mttmp); // and replace it with the updated one
strcpy(mtdirs, mttmp); // Restore the name
free(mttmp);
}
// Copy size bytes from file is to file os. The copy is done in blocks - the
// maximum memory can handle, up to 32k (INT_MAX).
void copy(ifstream& is, ofstream& os, long size) {
if (size <= 0) return;
char *buffer;
int num = 1; // Number of blocks to perform the copy
int block = (size > INT_MAX ? INT_MAX : int(size)); // (int to remove warning)
while (!(buffer = new char[block])) { // Determine the largest block size
num <<= 1; // Halve it each time
block = (block >> 1) + 1;
}
for (int j = --num; j > 0; j--) { // Copy the full-size blocks
is.read(buffer, block);
os.write(buffer, block);
}
block = int(size - num*block); // Copy what's left over
is.read(buffer, block); // (Again, int to remove the warning)
os.write(buffer, block);
delete []buffer;
}
// Create the directory structure (see finddirs, index and sort for details).
// If drives is an empty string then scan the drives already in the file,
// otherwise scan those drives specified (invalid drives will be ignored).
void dirfile(char* drives) {
char drv[26], orig[MAXDIR]; // Drives to scan, original dir.
int n = strlen(strupr(drives)); // Number of drives to scan
long last = 0; // Last drive position
if (n) { // Specified drives
strcpy(drv, drives);
cout << "Creating";
}
else { // Drives already there
ifstream mtin; open(mtin);
do {
mtin.seekg(last);
mtin.get(drv[n++]);
rlong(mtin, last);
} while (last);
mtin.close();
cout << "Updating";
}
cout << " \"" << mtdirs << "\".\n";
ofstream mt; open(mt);
dirs = new char*[MaxDirs]; // Create the array of directories
orig[0] = '\\';
for (int j = 0; j < n; j++) {
cout << "Drive " << drv[j] << ' ';
if (setdrive(drv[j])) {
cout << "will be ignored.\n";
continue;
}
getcurdir(0, orig+1);
chdir("\\");
dirnum = 0;
finddirs(); // Get all the directories
chdir(orig);
cout << "has " << dirnum << " directories.\n";
qsort((void*)dirs, dirnum, sizeof(dirs[0]), sort);
last = index(mt, drv[j]);
}
mt.seekp(last); // Write zero for last drive
wlong(mt, zero);
mt.close();
delete []dirs;
setdrive(*olddir); // Restore current drive
chdir(olddir); // and directory
}
// Starting from the current directory recursively find all directories for
// the current drive and store them as a reverse path separated by slashes.
// eg: "\language\bc" will be stored as "bc/language".
// Global variables dirs holds the directories; dirnum has the number found.
void finddirs() {
struct ffblk dir;
static int done;
static char path[MAXDIR];
getcurdir(0, path);
if (*path) { // Don't store the root directory
reverse(0, path); // (it's a null string)
dirs[dirnum++] = strdup(newdir);
}
for (done = finddirec(dir, "*.*"); !done; done = finddirec(dir)) {
chdir(dir.ff_name);
finddirs(); // Get any subdirectories
chdir(".."); // Back to this one
}
}
// Find a directory that matches name, ignoring the . and .. directories.
// If name is not given, continue the find.
// Returns the same as findfirst/findnext.
int finddirec(struct ffblk& dir, const char* name) {
int found;
found = (*name ? findfirst(name, &dir, FA_DIREC) : findnext(&dir));
while (found == 0 && (dir.ff_attrib != FA_DIREC || *dir.ff_name == '.'))
found = findnext(&dir);
return found;
}
// Write and index the directories for drive. First write the drive letter and
// an index to the next drive. Following this are 26 indices for each letter of
// the alphabet. An index of zero means no directories start with that letter.
// Then the directories, one per line, and a blank line to finish.
// It returns the position of the next drive index so zero can be written to
// indicate no more drives. However, os is left at the end of the file.
long index(ofstream& os, char drive) {
char let = 'A'; // Current index letter
long index, lin, din; // File positions
os.put(drive); // Write the drive letter
din = os.tellp(); // The next drive index position
wlong(os, zero); // Where the next drive will start
lin = os.tellp(); // The current letter index position
for (int j = 0; j < 26; j++) wlong(os, zero); // Letters' index positions
for (j = 0; j < dirnum; j++) {
while (*dirs[j] > let) { // In case there are no directories
let++; // that start with let
lin += sizeof(long);
}
if (*dirs[j] == let) { // Write the index for this letter
index = os.tellp(); // This is where it begins
os.seekp(lin); // Go to letter index position
wlong(os, index); // Write the value
os.seekp(0, ios::end); // Back to the end
let++; // Next letter
lin += sizeof(long); // and its index position
}
os << dirs[j] << endl; // Write the directory
free(dirs[j]); // No longer required
}
os << endl; // No more directories
index = os.tellp(); // Store the position for next drive
os.seekp(din);
wlong(os, index);
os.seekp(0, ios::end); // Ready for next drive
return din; // For writing 0 for no more drives
}
// This determines how the directories will be searched. Smaller paths are
// placed before larger paths, non-alphabetical entries are before alphabetical
// directories, and the directories are sorted alphabetically.
int sort(const void *a, const void *b) {
char *dir1 = *(char**)a, // qsort thinks I am sorting an array
*dir2 = *(char**)b; // of pointers - this gets the string
if (*dir1 == *dir2) { // Both start with the same character
int len1 = subs(dir1), // so sort by path length
len2 = subs(dir2);
if (len1 < len2) return -1; // Same size path length will be
else if (len1 > len2) return 1; // sorted by name below
}
int let1 = isalpha(*dir1), // First directory is alphabetic
let2 = isalpha(*dir2); // Second directory is alphabetic
if ((let1 && let2) || (!let1 && !let2)) // Both are/are not letters
return strcmp(dir1, dir2); // so sort by name
return (let1 ? 1 : -1); // Letters placed after non-letters
}
// Determine the number of directories in a path for the sort routine.
int subs(const char* path) {
int n = 0; // Number of slashes
for (; *path; path++) if (*path == '/') n++;
return n+1; // Number of paths
}
// Display the help screen.
void help() {
cout << // One really long string
"\n"
"mt - Move To drive/directory. mtmem - resident memory for mt.\n"
"Copyright 1996-7 Jason Hood. Freeware. Version "version".\n"
"\n"
"mt Display the current status.\n"
"mt #[#]ext Use file \"mtdirs.ext\" for the directory structure\n"
" [and make it permament].\n"
"mt @dc Create directory structure for drives D: and C:.\n"
"mt @ Update directory structure (rescan drives).\n"
"mt .... Equivalent to \"mt ..\\..\\..\".\n"
"mt;[;...|n] Move to a previous directory (n = 0 is current directory).\n"
"mt wg* Move to first subdirectory starting with \"wg\".\n"
"mt w/g Move to subdirectory \"w\" subsubdirectory \"g\".\n"
"mt wg Move to subdirectory \"wg\". If that fails search\n"
" for a directory that starts with \"wg\".\n"
"mt w g Search for a directory starting with \"w\" that\n"
" has a subdirectory starting with \"g\".\n"
"mt w g /h As above, then append subdirectory \"h\".\n"
"mt n,partial Select the nth match (or n+1 if already in nth).\n"
"mt + Add the current directory and its subdirs to the file.\n"
"mt +[+]path Create path if necessary, add it to the file [and move to it].\n"
"mt -[-]path Remove path from the file [but don't delete it from disk].\n"
"\n"
"Searches always begin from the root directory.\n";
}